home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998…eptember: Technology Seed / September 98 ADC Seed CD.toast / LaserWriter 8.6b5 Seed / Prerelease Docs / sample JPEGConverter / Source / sample ConverterShell.c next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  29.4 KB  |  829 lines  |  [TEXT/CWIE]

  1. /*
  2. *    File Name:        sample ConverterShell.c
  3. *
  4. *    Description:    This file contains a shell for building low level converters.
  5. *
  6. *    Written by:        David Gelphman    2/09/98
  7. *
  8. *    Copyright:    © 1998 by Apple Computer Inc., all rights reserved.
  9. *    
  10. *
  11. *    Change History (most recent first):
  12. *
  13. *        04/01/98    DMG457    First version.
  14. */
  15.  
  16. #include <Types.h>
  17. #include <TextUtils.h>
  18. #include <Resources.h>
  19. #include <CodeFragments.h>
  20.  
  21. #include "Debug.h"
  22. #include "sample ConverterShell.h"
  23. #include "DownloadMgrLib.h"
  24. #include "FeatureUtilsLib.h"
  25. #include "PSWriterErr.h"
  26. #include "Utilities.h"
  27. #include "Version.h"
  28.  
  29. /************ Defines ************/
  30.  
  31. #define DSC30Version 0x30000        // Fixed(3);
  32. #define EPSF30Version 0x30000        // Fixed(3);
  33.  
  34. #define doOutputPosition(subsectionString, info)    OutputPosition(comm, hints, (subsectionString), (info), isNotEPS)                            
  35.  
  36.  
  37. /************ Prototypes ************/
  38.  
  39. static OSStatus GetCollectionStrOrDef(Collection hints, CollectionTag tag, long id,
  40.                             StringPtr str, long strSize, StringPtr defaultStr);
  41.  
  42. static OSErr emitHintStr(StreamInfoData comm, const SubsectionStr *form, Collection hints, CollectionTag tag, long id,  
  43.                             ConstStringPtr defaultStr);                                                    
  44. static OSStatus headerDate(StreamInfoData comm);                                                    
  45.  
  46. static OSStatus    OutputPosition(StreamInfoData comm, Collection hints, const SubsectionStr *subsectionStr, void *info, Boolean isNotEPS);        
  47. OSErr InitLowConverterLib(CFragInitBlockPtr initBlkPtr);                                                    
  48. static UInt32 computeLanguageLevel(SInt32 hintLanguageLevel);
  49.  
  50.  
  51. /********* Static Global Data *********/
  52.  
  53. static const struct SubsectionStr
  54.     psLowVersion =                 {"\p%!PS-Adobe-3.0\r", kSubPSAdobe},
  55.     psLowEPSFVersion =             {"\p%!PS-Adobe-3.0 EPSF-3.0\r", kSubPSAdobeEPS},
  56.     psLowBoundingBox =             {"\p%%BoundingBox: 0 0 ^d ^d\r", kSubBoundingBox},
  57.     psLowBeginProlog =             {"\p%%BeginProlog\r", kSubBeginProlog},
  58.     psLowEndComments =             {"\p%%EndComments\r", kSubEndComments},    
  59.     psLowEndProlog =             {"\p%%EndProlog\r", kSubEndProlog},
  60.     psLowBeginSetup =             {"\p%%BeginSetup\r", kSubBeginSetup},
  61.     psLowEndSetup =                {"\p%%EndSetup\r", kSubEndSetup},
  62.     psLowPage =                    {"\p%%Page: ^d ^d\r", kSubPage},    
  63.     psLowEOF =                     {"\p%%EOF\r", kSubEOF},    
  64.     psLowBeginPageSetup =         {"\p%%BeginPageSetup\r", kSubBeginPageSetup},    
  65.     psLowEndPageSetup =         {"\p%%EndPageSetup\r", kSubEndPageSetup}, 
  66.     psLowPageTrailer =             {"\p%%PageTrailer\r", kSubPageTrailer},
  67.     psLowDocTrailer =             {"\p%%Trailer\r", kSubTrailer},
  68.     psLowPagesKnown =             {"\p%%Pages: ^d\r",    kSubPages},    
  69.     psLowLanguageLevel =        {"\p%%LanguageLevel: ^d\r", kSubLangLevel},    
  70.     psLowDocDataClean7Bit =        {"\p%%DocumentData: Clean7Bit\r", kSubDocData},
  71.     psLowDocDataBinary =        {"\p%%DocumentData: Binary\r", kSubDocData},
  72.     psLowDocTitle =                {"\p%%Title: (^T)\r", kSubTitle},
  73.     psLowDocFor =                {"\p%%For: (^T)\r", kSubFor},    
  74.     psLowDocRouting =            {"\p%%Routing: (^T)\r", kSubRouting},
  75.     psLowDocCreationDate =        {"\p%%CreationDate: (^T)\r", kSubDate},    
  76.     psLowDocCreator =            {"\p%%Creator: (^T)\r", kSubCreator},    
  77.     psLowShowPage =                {"\p\rshowpage\r", kSubShowpage};
  78.  
  79. static const unsigned char psBeforeDeviceMatrixAdjust[] = "\p[\r";        
  80. static const unsigned char psAfterDeviceMatrixAdjust[] = "\pcounttomark 1 eq{concat}{cleartomark}ifelse\r";
  81.  
  82. static FSSpec gLowConverterFileSpec;
  83.  
  84. // end of global data
  85.  
  86. OSErr InitLowConverterLib(CFragInitBlockPtr initBlkPtr)
  87. /*    Called before the Low Level Converter Library is loaded. We use this chance
  88.     to save away our FSSpec.
  89. */
  90. {
  91.     OSErr err = noErr;
  92.     
  93. /*    If this library was loaded from a file, then save away the file's
  94.     FSSpec. This way we can get to the library's resource fork to
  95.     get our resources.
  96. */
  97.     if(initBlkPtr->fragLocator.where == kDataForkCFragLocator){
  98.         gLowConverterFileSpec = *initBlkPtr->fragLocator.u.onDisk.fileSpec;
  99.     }else{
  100.         err = -1;
  101.     }
  102.         
  103.     return err;
  104. }
  105.  
  106. #if PRAGMA_IMPORT_SUPPORTED
  107. #pragma export on
  108. #endif
  109.  
  110.  
  111. OSStatus psLowGetConverterInformation(const ConverterDescription **theDescriptionPtr)
  112. {
  113.     OSStatus err = noErr;
  114.     ConverterDescription *thePtr;
  115.  
  116.     err = converterGetConverterInfoPtr(&thePtr);                    // ask the converter
  117.  
  118.     if(theDescriptionPtr)
  119.         *theDescriptionPtr = err ? NULL : thePtr;
  120.             
  121.     return err;
  122. }
  123.  
  124. OSStatus psLowCanConvert(PSStream *stream, Collection hints, LowConverterInfo *dataInfo, Fixed *downloadability)
  125. {
  126.     OSStatus err = noErr;
  127.     PSSerialStream *readStream;
  128.  
  129.     if(stream->type == kPSRandomAccessStream)
  130.         readStream = &(stream->u.file.serialStream);
  131.     else{
  132.         if(stream->type == kPSSerialStream)
  133.             readStream = &stream->u.ps;
  134.         else{
  135.             err = errUnsupportedStream;    
  136.         }
  137.     }
  138.     
  139.     *downloadability = CANTDOWNLOAD;            // start by saying we can't download this file
  140.     
  141.     if(!err){
  142.         err = converterCanConvert(readStream, stream, hints, dataInfo, downloadability);
  143.     }
  144.         
  145.     if(err == errUnsupportedStream){
  146.         err = noErr;
  147.         *downloadability = CANTDOWNLOAD;
  148.     }    
  149.     
  150.     return err;
  151. }
  152.  
  153. OSStatus psLowGetStreamInfo(PSStream *stream, Collection hints, DownloadDocumentInfo *downloadDocInfo)
  154. /*    Here we return info about the document/stream.
  155. */
  156. {
  157.     OSStatus err = noErr;
  158.     kHintJobTypeVar jobType;    
  159.     kHintCopiesVar copies = 1;    
  160.     PSSerialStream *readStream;
  161.     SInt32 numPages = 1;                    // Here we assume there is only one page. Converters that
  162.                                             // have more than one page need to determine the correct number
  163.                                             // and use it in place of this hard coded constant. If EPS was requested
  164.                                             // then the number of pages should always be 1 and the first of multiple
  165.                                             // pages is the only one that should be generated.
  166.     OSType docType = '????';
  167.                         
  168.     if(stream->type == kPSRandomAccessStream)
  169.         readStream = &(stream->u.file.serialStream);
  170.     else{
  171.         if(stream->type == kPSSerialStream)
  172.             readStream = &stream->u.ps;
  173.         else{
  174.             err = errUnsupportedStream;
  175.         }
  176.     }
  177.  
  178.     // determine what type of job was requested
  179.     if(!err){
  180.         err = getHint(hints, kHintJobTypeTag, kHintJobTypeId, sizeof(jobType), &jobType);
  181.         if(err == collectionItemNotFoundErr){
  182.             err = noErr;
  183.             jobType = psJobPostScript;
  184.         }
  185.     }
  186.  
  187.     if(!err && jobType != psJobEPSNoPreview){
  188.         // if we're not generating EPS, then the number of copies might be something other than 1.
  189.         err = getHint(hints, kHintCopiesTag, kHintCopiesId, sizeof(copies), &copies);        
  190.         if(err == collectionItemNotFoundErr){
  191.             copies = kHintCopiesDef;
  192.             err = noErr;
  193.         }
  194.     }
  195.  
  196.     if(!err){
  197.         // if we are not generating EPS we use psRequiresManualFeed in FeatureUtilsLib to look
  198.         // at the hints collection and tell us whether the job requires manual feed. EPS jobs are
  199.         // never manual feed.
  200.         if(jobType != psJobEPSNoPreview){
  201.             err = psRequiresManualFeed(hints, &(downloadDocInfo->isManualFeed));
  202.         }else{
  203.             downloadDocInfo->isManualFeed = false;
  204.         }
  205.     }
  206.     
  207.     // ask the shell client code what its document type is.
  208.     if(!err){
  209.         err = converterGetConverterDocType(readStream, stream, hints, &docType);
  210.     }
  211.     
  212.     // fill in the data we collected
  213.     downloadDocInfo->type = docType;
  214.     downloadDocInfo->pages = numPages;
  215.     downloadDocInfo->copies = copies;    
  216.     
  217.     return err;
  218. }
  219.  
  220. OSStatus psLowPeekConvert(PSStream *stream, Collection hints)
  221. {
  222.     OSStatus err = noErr;
  223.     PSSerialStream *readStream;
  224.  
  225.     if(stream->type == kPSRandomAccessStream)
  226.         readStream = &(stream->u.file.serialStream);
  227.     else{
  228.         if(stream->type == kPSSerialStream)
  229.             readStream = &stream->u.ps;
  230.         else{
  231.             err = errUnsupportedStream;
  232.         }
  233.     }
  234.  
  235.     if(!err)
  236.         err = converterPeekConvert(readStream, stream, hints);
  237.                     
  238.     return err;
  239. }
  240.  
  241. #define BACKCHANNEL_BUFFER_SIZE 0x200
  242. OSStatus psLowDoConvert(PSStream *streamIn, PSStream *streamOut, Collection hints)
  243. {
  244.     unsigned char *inDataBuffer = NULL;
  245.     OSStatus err = noErr;
  246.     PSSerialStream *readStream;
  247.     Boolean doBinary = false, canDoGrayOnHost = false;
  248.     UInt32 languageLevel = 1;
  249.     Str255 converterName = "\p";
  250.  
  251.     if(streamIn->type == kPSRandomAccessStream)
  252.         readStream = &(streamIn->u.file.serialStream);
  253.     else{
  254.         if(streamIn->type == kPSSerialStream)
  255.             readStream = &streamIn->u.ps;
  256.         else{
  257.             err = errCantHandleThisDownloadData;
  258.         }
  259.     }
  260.  
  261.     if(!err){
  262.         err = converterGetConverterName(converterName);                    // get the name from the converter
  263.     }
  264.     
  265.     if(!err){
  266.         PSPagesType thePages;
  267.         thePages.pages = 1;                        // Again we always have 1 page for this shell code.
  268.                                                 // This should be adjusted if you have more than one page.
  269.         thePages.order = kPSOrderAscending;        
  270.         if(!err)
  271.             err = AddCollectionItem(hints, kHintPagesTypeTag, kHintPagesTypeId, sizeof(thePages), &thePages);
  272.     }
  273.     
  274.     // allocate our back channel buffer so that we can read from the backchannel if there is one
  275.     if(!err){
  276.         inDataBuffer = (unsigned char *)psNewPtr(BACKCHANNEL_BUFFER_SIZE);
  277.         if(!inDataBuffer){
  278.             err = MemError();
  279.             if(!err)err = memFullErr;
  280.         }
  281.     }
  282.  
  283.     if(!err && streamIn && streamOut){            
  284.                                                 // developers should adjust this if there is more than one page
  285.         SInt32 numPages = 1;                    // we always have one page in this shell code
  286.         
  287.         SInt32 pageNumber = 1;                    // when we emit a page it is always the 1st page
  288.         Boolean isNotEPS = true;
  289.         StreamInfoData comm = NULL;
  290.  
  291.         // process the results of our query. We initialize our data to our defaults so that we
  292.         // operate as expected if for some reason our query hints aren't available
  293.         kHintLanguageLevelVar langlevel = kHintLanguageLevelDef;
  294.         kHintDataFormatVar eightBit = false;
  295.         kHintTransparentChannelVar transparent = false;
  296.         kHintColorDeviceVar isColorDevice = kHintColorDeviceDef;
  297.         kHintColorSepVar doingColorSeps = kHintColorSepDef;
  298.         
  299.         err = getHint(hints, kHintLanguageLevelTag, kHintLanguageLevelId, sizeof(langlevel), &langlevel);
  300.         if(err == collectionItemNotFoundErr)err = noErr;
  301.         if(!err)err = getHint(hints, kHintEighthBitTag, kHintDataFormatId, sizeof(eightBit), &eightBit);
  302.         if(err == collectionItemNotFoundErr)err = noErr;
  303.         if(!err)err = getHint(hints, kHintTransparentChannelTag, kHintTransparentChannelId, sizeof(transparent), &transparent);
  304.         if(err == collectionItemNotFoundErr)err = noErr;
  305.  
  306.         if(!err)err = getHint(hints, kHintColorDeviceTag, kHintColorDeviceId, sizeof(isColorDevice), &isColorDevice);
  307.         if(err == collectionItemNotFoundErr)err = noErr;
  308.  
  309.         if(!err)err = getHint(hints, kHintColorSepTag, kHintColorSepId, sizeof(doingColorSeps), &doingColorSeps);
  310.         if(err == collectionItemNotFoundErr)err = noErr;                        
  311.         
  312.         if(!err){
  313.             kHintEPSBBoxVar bbox;
  314.             void *clientData = NULL;
  315.             doBinary = eightBit && transparent;        // we can only do binary if we are transparent and 8 bit
  316.  
  317.             /*     computeLanguageLevel computes the minimum PostScript language level we must support. Negative values
  318.                 of langlevel returned from the query typically correspond to save to disk options where we are
  319.                 to generate output compatible with a specific language level but it may be sent to a device which
  320.                 has a language level which is higher. This gives the PostScript producing code a chance to generate
  321.                 conditional code that takes advantage of advanced features while emulating those features in the
  322.                 target miminum language level. Here we simply use the minimum language level
  323.             */
  324.             languageLevel = computeLanguageLevel(langlevel); 
  325.             
  326.             // can only do gray downsampling on host if the destination is definitely a BW printer 
  327.             // and it definitely isn't doing color seps
  328.             canDoGrayOnHost =  (doingColorSeps == kTriFalse) && (isColorDevice == kTriFalse);
  329.  
  330.             // determine whether we are doing EPS or PS
  331.             if(!err){
  332.                 kHintJobTypeVar jobType = psJobEPSNoPreview;
  333.                 err = getHint(hints, kHintJobTypeTag, kHintJobTypeId, sizeof(jobType), &jobType);
  334.                 if(err == collectionItemNotFoundErr){
  335.                     err = noErr;
  336.                     jobType = psJobPostScript;
  337.                 }
  338.                 
  339.                 isNotEPS = jobType == psJobPostScript;
  340.             }
  341.             
  342.             // get a StreamInfoData structure
  343.             if(!err){
  344.                 err = psSetupStreamInfoData(&comm, streamOut, hints);
  345.                 if(!err){
  346.                     OSStatus tempErr = noErr;
  347.                     
  348.                     /*     let the client initialize its data. The client should collect whatever information it needs
  349.                         to tell us the bounding box and generate its prolog, etc.
  350.                         
  351.                         Note that we are passing a POINTER to the language level which we expect to be filled in by
  352.                         the converterInitDoConvertClientData routine to be the lowest languagelevel (greater than 0!)
  353.                         that it is supporting.                    
  354.                     */
  355.                     err = converterInitDoConvertClientData(&clientData, 
  356.                                 readStream, streamIn, streamOut, hints,
  357.                                 inDataBuffer, BACKCHANNEL_BUFFER_SIZE,
  358.                                 &languageLevel, doBinary, canDoGrayOnHost, isNotEPS);
  359.                     
  360.                     if(!err){                                                        
  361.                         
  362.                         // let the client tell us what the bounding box is for this data. That bbox is used
  363.                         // to generate the autoscaling or %%BoundingBox comment, whichever is appropriate. 
  364.                         if(!err){    
  365.                             err = converterGetBBox(&bbox, clientData);
  366.                         }
  367.             
  368.                         // we only add this hint if we are not doing EPS
  369.                         if(!err && isNotEPS){
  370.                             err = AddCollectionItem(hints, kHintEPSBBoxTag, kHintScalingBBoxId, sizeof(bbox), &bbox);
  371.                         }    
  372.                                 
  373.                         // these hints are potentially used by coverpage code to generate the application information 
  374.                         // for the cover sheet
  375.                         if(!err){                                                                                    
  376.                             Str255 appNameString, tempStr;
  377.                             (void)pStrCopy(appNameString, converterName);
  378.             
  379.                             err = AddCollectionItem(hints, kHintAppNameTag, kHintAppNameId, sizeof(appNameString), appNameString);
  380.             
  381.                             if(!err)err = AddCollectionItem(hints, kHintClientNameTag, kHintClientNameId, sizeof(appNameString), appNameString);
  382.             
  383.                             CopyCtoPstr(tempStr, kShortVersStr);
  384.                             (void)pStrCopy(appNameString, tempStr);
  385.             #if qDebug
  386.                             CopyCtoPstr(tempStr, kqDebug);
  387.                             (void)pStrCat(appNameString, tempStr);
  388.             #endif
  389.                             if(!err)err = AddCollectionItem(hints, kHintClientVersionTag, kHintClientVersionId, sizeof(appNameString), appNameString);
  390.                         }                                                                                            
  391.             
  392.                         if(!err){
  393.                             // emit the PostScript code necessary to handle the image
  394.                             
  395.                             // write the %!PS-Adobe-3.0 comment appropriately.                        
  396.                             if(isNotEPS){
  397.                                 Fixed dscVersion = DSC30Version;
  398.                                 err = doOutputPosition(&psLowVersion, &dscVersion);                                        
  399.                             }else{
  400.                                 EPSFVersion epsfVersion;
  401.                                 epsfVersion.dscVersion = DSC30Version;
  402.                                 epsfVersion.epsfVersion = EPSF30Version;
  403.             
  404.                                 err = doOutputPosition(&psLowEPSFVersion, &epsfVersion);                            
  405.                                 if(!err){
  406.                                     DSCBBox dscBBox;
  407.                                     dscBBox.llx = bbox.left;
  408.                                     dscBBox.lly = bbox.bottom;
  409.                                     dscBBox.urx = bbox.right;
  410.                                     dscBBox.ury = bbox.top;
  411.                                     err = psOutFormatPositionInfo(comm, &psLowBoundingBox, &dscBBox, (long)dscBBox.urx, (long)dscBBox.ury);
  412.                                 }
  413.                             }                                                                                        
  414.                             
  415.                             // emit the data for the header including the creator
  416.                             if(!err)err = emitHintStr(comm, &psLowDocTitle, hints, kHintDocNameTag, kHintDocNameId, kHintDocNameDef);
  417.                             if(!err)err = emitHintStr(comm, &psLowDocFor, hints, kHintUserNameTag, kHintUserNameId, kHintUserNameDef);
  418.                             if(!err)err = emitHintStr(comm, &psLowDocRouting, hints, kHintRoutingTag, kHintRoutingId, NULL);
  419.                             if(!err)err = headerDate(comm);
  420.                             if(!err){
  421.                                 Str255 creatorString, tempStr;
  422.                                 (void)copyPStr(creatorString, converterName, sizeof(creatorString));
  423.                                 (void)pStrCat(creatorString, "\p v");
  424.                                 CopyCtoPstr(tempStr, kShortVersStr);
  425.                                 (void)pStrCat(creatorString, tempStr);
  426. #if qDebug
  427.                                 CopyCtoPstr(tempStr, kqDebug);
  428.                                 (void)pStrCat(creatorString, tempStr);
  429. #endif
  430.                                 err = psOutFormatPositionInfo(comm, &psLowDocCreator, creatorString, creatorString);        
  431.                             }
  432.                             
  433.                             // this reflects the total number of pages in the document. For this sample code it
  434.                             // is hard coded to 1 page
  435.                             if(!err)err = psOutFormatPositionInfo(comm, &psLowPagesKnown, &numPages, numPages);
  436.                             
  437.                             // if we are generating PostScript language output for a language level greater than Level 1
  438.                             // then we generate the %%LanguageLevel comment
  439.                             if(!err && languageLevel > 1){
  440.                                 err = psOutFormatPositionInfo(comm, &psLowLanguageLevel, &languageLevel, languageLevel);    
  441.                             }
  442.                             
  443.                             // generate the %%DocumentData comment based on whether we are doing binary or not
  444.                             if(!err){
  445.                                 if(doBinary){
  446.                                     DSCDocumentData myData = kDSCBinary;
  447.                                     err = psOutFormatPositionInfo(comm, &psLowDocDataBinary, &myData);            
  448.                                 }else{
  449.                                     DSCDocumentData myData = kDSCClean7Bit;
  450.                                     err = psOutFormatPositionInfo(comm, &psLowDocDataClean7Bit, &myData);                
  451.                                 }
  452.                             }
  453.                             
  454.                             
  455.                             if(!err)err = doOutputPosition(&psLowEndComments, NULL);                        
  456.             
  457.                             if(!err)err = doOutputPosition(&psLowBeginProlog, NULL);                        
  458.             
  459.                             // let the converter client emit its prolog
  460.                             if(!err)err = converterEmitProlog(comm, clientData);                                
  461.  
  462.                             if(!err)err = doOutputPosition(&psLowEndProlog, NULL);                            
  463.             
  464.                             if(!err)err = doOutputPosition(&psLowBeginSetup, NULL);                            
  465.                 
  466.                             if(!err)err = doOutputPosition(&psLowEndSetup, NULL);                            
  467.                                 
  468.                                 
  469.                             // the pageNumber used here reflects the number of the page that we are about to emit the
  470.                             // PostScript code for. This is the %%Page: comment. Since we are hard coded to do 1 page
  471.                             // we use the pageNumber here which was hardcoded to 1.
  472.                             if(!err){
  473.                                 DSCPage dscPageStatus;
  474.                                 
  475.                                 dscPageStatus.ordinal = pageNumber;
  476.                                 NumToString(pageNumber, dscPageStatus.label);            
  477.                             
  478.                                 err = psOutFormatPositionInfo(comm, &psLowPage, &dscPageStatus, pageNumber, pageNumber);
  479.                             }
  480.             
  481.                             // we tell the output position call what page we are on so it can do the right thing for that
  482.                             // page
  483.                             if(!err){
  484.                                 err = doOutputPosition(&psLowBeginPageSetup, &pageNumber);    
  485.                             }
  486.             
  487.                             /*     for a one page graphic converter we can do our device adjust matrix before our other coordinate
  488.                                 transformations for that page. For a multiple page converter we might have to be more careful
  489.                                 if we create a matrix in the job setup section that we concat for each page.
  490.                             */
  491.                             if(!err){                                                                            
  492.                                 err = psOutPStr(comm, psBeforeDeviceMatrixAdjust);
  493.                                 if(!err)err = psWriteSubsectionFeature(comm, hints, kSubDeviceAdjustMatrix, NULL, kBeforeSubsection, isNotEPS);    
  494.                                 if(!err)err = psOutPStr(comm, psAfterDeviceMatrixAdjust);
  495.                             }                                                                                    
  496.  
  497.                             /*    Here we generate our request for auto scaling. The feature utils lib code for autoscaling 
  498.                                 handles EPS correctly (i.e. generates no autoscaling code) and it also handles the hint
  499.                                 kHintDoAutoScalingTag properly. Still, some converters may not want autoscaling and they
  500.                                 should remove this code if that is the case.                            
  501.                             */
  502.                             if(!err)
  503.                                 err = psWriteSubsectionFeature(comm, hints, kSubAutoScaling, NULL, kBeforeSubsection, isNotEPS);                                        
  504.             
  505.                             
  506.                             if(!err){
  507.                                 err = doOutputPosition(&psLowEndPageSetup, &pageNumber);                                    
  508.                             }
  509.                         }
  510.                         
  511.                         // ask the shell client to generate the data for this page
  512.                         if(!err){
  513.                             err = converterEmitPageData(comm, clientData);    
  514.                         }
  515.                             
  516.                         // make sure the backchannel data is read properly.
  517.                         if(!err)
  518.                             err = ReadWriteBackChannel(streamIn, readStream->write, streamOut, streamOut->u.ps.read, inDataBuffer, BACKCHANNEL_BUFFER_SIZE);
  519.                                 
  520.                         if(!err){
  521.                             // emit the PostScript code necessary to complete the image. If the shell client needs
  522.                             // to generate showpage then this line should be removed
  523.                             err = psOutFormatPosition(comm, &psLowShowPage);                
  524.                             
  525.                             // emit the page trailer data
  526.                             if(!err)
  527.                                 err = doOutputPosition(&psLowPageTrailer, &pageNumber);                
  528.                             
  529.                             // emit the document trailer data
  530.                             if(!err)
  531.                                 err = doOutputPosition(&psLowDocTrailer, NULL);                
  532.             
  533.                             // We're done so write the EOF. 
  534.                             if(!err)
  535.                                 err = doOutputPosition(&psLowEOF, NULL);                
  536.                             
  537.                             // read any last data.
  538.                             if(!err)
  539.                                 err = ReadWriteBackChannel(streamIn, readStream->write, streamOut, streamOut->u.ps.read, inDataBuffer, BACKCHANNEL_BUFFER_SIZE);
  540.             
  541.                         }
  542.                         // give the client a chance to clean up its data
  543.                         if(clientData){
  544.                             tempErr = converterDisposeDoConvertClientData(clientData);
  545.                             clientData = NULL;
  546.                             if(!err)err = tempErr;
  547.                         }            
  548.                     }    
  549.                     tempErr = psDisposeStreamInfoData(&comm);                                
  550.                     if(!err)err = tempErr;                                                    
  551.                 }
  552.             }
  553.         }
  554.     }
  555.     
  556.     psDisposePtr(inDataBuffer);            
  557.                 
  558.     return err;
  559. }
  560. #undef BACKCHANNEL_BUFFER_SIZE
  561.  
  562. OSErr psLowGetConverterVersion(struct CFMVersion *version)
  563. {
  564.     return converterGetVersion(version);
  565. }
  566.  
  567. OSStatus psLowAddConverterQueries(Collection hints, Collection query)
  568. /*    Here we decide what queries we need to add. 
  569. */
  570. {
  571.     OSStatus err = noErr;
  572.     kHintLanguageLevelVar defaultLevel = kHintLanguageLevelDef;
  573.     Unused(hints);
  574.  
  575.     err = AddCollectionItem(query, kHintLanguageLevelTag, kHintLanguageLevelId, sizeof(defaultLevel), &defaultLevel);
  576.     if(!err){
  577.         kHintDataFormatVar eightBit = false;            // conservative defaults
  578.         err = AddCollectionItem(query, kHintEighthBitTag, kHintEighthBitId, sizeof(eightBit), &eightBit);
  579.     }
  580.     if(!err){
  581.         kHintTransparentChannelVar transparent = false;    // conservative defaults
  582.         err = AddCollectionItem(query, kHintTransparentChannelTag, kHintTransparentChannelId, sizeof(transparent), &transparent);
  583.     }
  584.                                                                                     // add color device related queries
  585.     if(!err){
  586.         kHintColorDeviceVar isColorDevice = kHintColorDeviceDef;        // unknown whether we are color device
  587.         err = AddCollectionItem(query, kHintColorDeviceTag, kHintColorDeviceId, sizeof(isColorDevice), &isColorDevice);
  588.     }
  589.  
  590.     if(!err){
  591.         kHintColorSepVar doingColorSeps = kHintColorSepDef;        // unknown whether we are doing color separations
  592.         err = AddCollectionItem(query, kHintColorSepTag, kHintColorSepId, sizeof(doingColorSeps), &doingColorSeps);
  593.  
  594.         // if the hint is already there and locked, then that is fine.
  595.         if(err == collectionItemLockedErr)err = noErr; 
  596.  
  597.     }
  598.  
  599.     if(!err)
  600.         err = converterAddAdditionalQueries(hints, query);
  601.  
  602.     return err;
  603. }
  604.  
  605.  
  606. #if PRAGMA_IMPORT_SUPPORTED
  607. #pragma export off
  608. #endif
  609.  
  610. OSStatus ReadWriteBackChannel(PSStream *streamToClient, PSWriteProc write, 
  611.                                         PSStream *streamToPrinter, PSReadProc read,
  612.                                         unsigned char *inDataBuffer, long bufferSize)
  613. {
  614.     OSStatus err = noErr;
  615.     if(read){
  616.         err = read(streamToPrinter, inDataBuffer, &bufferSize);
  617.         // write any data we read from backchannel back out to client
  618.         if(!err && bufferSize && write)err = write(streamToClient, inDataBuffer, bufferSize);    
  619.     }
  620.     return err;
  621. }
  622.  
  623. static OSErr emitHintStr(StreamInfoData comm, const SubsectionStr *form, Collection hints, CollectionTag tag, long id, 
  624.                             ConstStringPtr defaultStr)
  625. /*    This function can be used to emit the PostScript specified by 'form' to 'comm.
  626.     The 'form' format string is supposed to have a Pascal string parameter. This
  627.     routine gets the Pascal string by looking in 'hints' for a hint specified by
  628.     'tag' and 'id'. If the hint does not exist then this routine will use the
  629.     default Pascal string supplied by 'defaultStr'. If 'defaultStr' is NULL then
  630.     nothing is emitted by this routine. This routine will only return an error
  631.     if there is a problem emitting a string. A missing hint does not return an error.
  632. */
  633. {
  634.     Str255 str;
  635.     OSStatus err;
  636.     
  637.     err = GetCollectionStrOrDef(hints, tag, id,    str, sizeof(str), (StringPtr)defaultStr);
  638.     if(!err){
  639.         err = psOutFormatPositionInfo(comm, form, str, str);        //
  640.     }else{
  641.         err = noErr;        // A missing hint isn't an error.
  642.     }
  643.     
  644.     return err;
  645. }
  646.  
  647. static OSStatus GetCollectionStrOrDef(Collection hints, CollectionTag tag, long id,
  648.                             StringPtr str, long strSize, StringPtr defaultStr)
  649. /*    Look for the Pascal String hint specified by 'tag' and 'id' in 'hints'.
  650.     Copy the string into the buffer pointed to by 'str'. The caller guarentees
  651.     that 'str' points to at least 'strSize' bytes. If the hint can not be found
  652.     and 'defaultStr' is NULL, then the err collectionItemNotFoundErr is returned.
  653.     If the hint is not found and 'defaultStr' is not NULL, then the pascal
  654.     string pointed to by 'defaultStr' is copied into 'str', truncated if necessary.
  655. */
  656. {
  657.     OSStatus err;
  658.     long itemSize = strSize;
  659.     
  660.     str[0] = 0;                                                        // Start with an empty string.
  661.     err = GetCollectionItem(hints, tag, id, &itemSize, str);
  662.     
  663.     if(err == collectionItemNotFoundErr && defaultStr != NULL){        // Use the default if we have to.
  664.         copyPStr(str, defaultStr, strSize);
  665.         err = noErr;
  666.     }
  667.     
  668.     return err;
  669. }
  670.  
  671.  
  672. static OSStatus headerDate(StreamInfoData comm)
  673. {
  674.     unsigned long timeNow;
  675.     Str63 str;
  676.     StringPtr spaceSpot;
  677.     
  678.     GetDateTime(&timeNow);
  679.     IUTimeString(timeNow, false, str);
  680.     spaceSpot = str + *str + 1;
  681.     IUDateString(timeNow, longDate, spaceSpot);
  682.     *str = (Byte)(*str + *spaceSpot + 1);
  683.     *spaceSpot = ' ';
  684.     
  685.     return psOutFormatPositionInfo(comm, &psLowDocCreationDate, str, str);
  686. }
  687.  
  688. static OSStatus    OutputPosition(StreamInfoData comm, Collection hints, const SubsectionStr *subsectionStr, void *info, Boolean isNotEPS)
  689. {
  690.     OSStatus err = noErr;
  691.         
  692.     err = psWriteSubsectionFeature(comm, hints, subsectionStr->subsection, info, kBeforeSubsection, isNotEPS);                                    
  693.     if(!err){
  694.         if(info)
  695.             err = psOutFormatPositionInfo(comm, subsectionStr, info);
  696.         else
  697.             err = psOutFormatPosition(comm, subsectionStr);
  698.     }
  699.     if(!err)err = psWriteSubsectionFeature(comm, hints, subsectionStr->subsection, info, kAfterSubsection, isNotEPS);                                    
  700.     
  701.     return err;
  702.  
  703. }
  704.  
  705. OSStatus writeLogMsg(PSStream *streamOut, PSSubsection subsection, void *info, SInt32 stringsID, SInt32 msgID, Boolean isError)
  706. /*    This routine is to be called by a converter to log any error or warning messages which are appropriate
  707.     during the data conversion. 
  708.  
  709.     streamOut is the write stream passed to the low level converter's converterInitDoConvertClientData proc
  710.     
  711.     subsection is the PSSubsection for which the error pertains. Use kSubAnon if there is no appropriate subsection 
  712.     
  713.     info is a pointer to a structure appropriate for the subsection being reported or is NULL.
  714.     
  715.     stringsID is the ID of a STR# resource containing the message string list for the converter. The
  716.                     converter library will be opened by writeLogMsg to obtain the STR# resource and use it so
  717.                     a client need not open the library before calling writeLogMsg.
  718.     
  719.     msgID is the list number of the target message within the STR# resource.
  720.     
  721.     isError is LOGERROR if the caller wants the message to be reported as an error as opposed to a warning. The
  722.             constant LOGWARNING is used to report the message as a warning.
  723. */
  724. {
  725.     OSStatus err = noErr;
  726.     PSSerialStream *stream;
  727.  
  728.     if(streamOut->type == kPSRandomAccessStream)
  729.         stream = &(streamOut->u.file.serialStream);
  730.     else{
  731.         if(streamOut->type == kPSSerialStream)
  732.             stream = &streamOut->u.ps;
  733.         else{
  734.             err = errCantHandleThisDownloadData;                                        
  735.         }
  736.     }                                                                                    
  737.     
  738.     if(!err){
  739.         DSCLogData logData;                                                                
  740.         short libRes, holdResFile;
  741.  
  742.     /*    The reason we open the library resource fork here rather than in the library init proc
  743.         is that we are using shared global data so our init proc only is called when the library
  744.         is initially opened. If we were to open the library and save the
  745.         fref, that would work for the caller who made the first call to the library, but any
  746.         subsequent caller into the already opened library wouldn't have the resource
  747.         fork in its resource chain and our resource calls would fail. Instead the shell code
  748.         saves the library FSSpec and we need to open (and close) the library when we need it.
  749.         No big deal but if we don't respect this, we'll have problems when there are multiple
  750.         downloads going on simultaneously.
  751.     */
  752.         holdResFile = CurResFile();
  753.         err = openLowLibraryResFile(&libRes);
  754.  
  755.         if(!err){
  756.             GetIndString(logData.logMessage, stringsID, msgID);            
  757.             CloseResFile(libRes);
  758.         }
  759.         UseResFile(holdResFile);
  760.  
  761.         // build the data to put in the info field of the PSPosition data for the call
  762.         // the subsection on the log data is whatever the caller asked for
  763.         logData.logSubsection = subsection;        
  764.         
  765.         // the info on the log data is whatever the caller    asked for                                                                                                                
  766.         logData.info = info;    
  767.         
  768.         // the subsection on our write depends whether the caller wanted it to be a warning or error message                                                                        
  769.         stream->pos.subsection = isError ? kSubLogErrorData : kSubLogWarningData;
  770.         
  771.         // the info field is the logdata that we created based on the callers request        
  772.         stream->pos.info = &logData;                                            
  773.         stream->pos.id++;                                        
  774.  
  775.         // we are writing no data but are doing the write just to pass along the data in the PSPosition on streamOut
  776.         err = stream->write(streamOut, NULL, 0);                                        
  777.  
  778.         // reset the PSPosition data after our write call
  779.         stream->pos.subsection = kSubAnon;
  780.         stream->pos.info = NULL;                                                        
  781.         stream->pos.id++;                                            
  782.  
  783.     }
  784.  
  785.     return err;
  786. }
  787.  
  788. OSErr openLowLibraryResFile(short *fRef)
  789. /*    Open the low level converter's resource fork using the permission flags
  790.     from 'permission'. Place the fRef of the open file into *'fRef'.
  791.     If the file can not be opened, then an error is returned and
  792.     *'fRef' is set to -1. The caller should use CloseResFile() to
  793.     close *'fRef'. rb840
  794. */
  795. {
  796.     OSErr err;
  797.         
  798.     *fRef = FSpOpenResFile(&gLowConverterFileSpec, fsRdPerm);
  799.     err = ResError();
  800.     if(err){
  801.         *fRef = -1;
  802.     }
  803.     
  804.     return err;
  805. }
  806.  
  807. static UInt32 computeLanguageLevel(SInt32 hintLanguageLevel)
  808. /*
  809.     This routine returns the minimum language level which is required by the hintLanguageLevel passed
  810.     in. hintLanguageLevel can correspond to requests to support multiple language levels, such as
  811.     "Level 1 Compatible" or "Level 2 Compatible" without specifying exactly what language level the target
  812.     device actually contains.
  813. */
  814. {
  815.     UInt32 languageLevel = hintLanguageLevel;
  816.     
  817.     if(hintLanguageLevel < 1){
  818.         // negative language level values mean different things
  819.         if(hintLanguageLevel <= Level1and2){
  820.             // this is level1and2, level2and3, level3and4, ...
  821.             languageLevel = -hintLanguageLevel - 1;
  822.         }else{
  823.             // this is language level other or unknown
  824.             languageLevel = 1;
  825.         }
  826.     }
  827.     return languageLevel;
  828. }
  829.